//patrick braga-henebry
//unwrap-headless
//use for condor
//don't use debug flag
//paradigms
//due 26 mar 2008
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include <cxcore.h>
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#define PI 3.1415926535

using namespace std;


class txt
{
public:
  txt();
  ~txt();
  string LoR;
  string filename;
  int pupcr;
  int pupcc;
  int puprad;
  int limcr;
  int limcc;
  int limrad;
};
txt::txt(){}
txt::~txt(){}

int main(int argc, char ** argv)
{
  char d = 0;
  char txtflag = 0;
  char maskflag = 0;
  char *outfilename = "outfile.pgm";
  int i,j,k;
  char *infilename;
  char *txtfilename;
  char *maskfilename; 
  int n=3;
  txt holdtext;
  /* process opts */
  while(1) 
  {
    int c = getopt(argc,argv,"do:i:t:m:x:y:r:X:Y:R:");
    if (c == -1) break;
    switch(c) 
    {
      case 'd': d = 1; break;
      case 'o': outfilename = optarg; break;
      case 'i': infilename = optarg; break;
      case 't': 
        txtfilename = optarg; 
        txtflag =1;
        break;
      case 'x': holdtext.pupcc = atoi(optarg); break;
      case 'y': holdtext.pupcr = atoi(optarg); break;
      case 'r': holdtext.puprad= atoi(optarg); break;
      case 'X': holdtext.limcc = atoi(optarg); break;
      case 'Y': holdtext.limcr = atoi(optarg); break;
      case 'R': holdtext.limrad= atoi(optarg); break;
      case 'm': 
        maskfilename = optarg; 
        maskflag = 1;
        break;
      case '?': fprintf(stderr,"FAIL: malformed option list\n");
    }
  };
  IplImage* src = cvLoadImage(infilename, CV_LOAD_IMAGE_UNCHANGED);
  
 
  IplImage* out = cvCreateImage( cvSize (480,40), IPL_DEPTH_8U, 1);
  
  IplImage* mask;
  
  IplImage* linetmp = cvCreateImage( cvSize (1,40), IPL_DEPTH_8U, 1);
  IplImage* linetmpbig; // to be size and re-sized later, as things progress
  cvZero(out);
  
  
  //cvNamedWindow("src", CV_WINDOW_AUTOSIZE);
  //cvShowImage("src", src);

  
  //cvWaitKey(0);
  if (d) {
    printf("%dx%d, %d-channel image file\n",src->height, src->width, src->nChannels);
  }
  
  if (txtflag )
  {
    //process the txt file
    ifstream txtStream;
    txtStream.open(txtfilename);
    if( !txtStream ) {
      cerr << "Error opening input stream" << endl;
      return 0;
    }
    txtStream >> holdtext.LoR;
    txtStream >> holdtext.filename;
    txtStream >> holdtext.pupcr;
    txtStream >> holdtext.pupcc;
    txtStream >> holdtext.puprad;
    txtStream >> holdtext.limcr;
    txtStream >> holdtext.limcc;
    txtStream >> holdtext.limrad;
  cout << holdtext.LoR << " " << holdtext.filename << " ";
  }
  
  //make or load the mask, req's the args
  if (maskflag)
  {
    mask = cvLoadImage(maskfilename, CV_LOAD_IMAGE_UNCHANGED);
  }
  else
  {
    mask = cvCreateImage(cvSize(src->width, src->height), IPL_DEPTH_8U, 1);
    cvZero(mask);
    //white limbic, then black pupilic
    cout << "ok here" << endl;
    cvCircle(mask,cvPoint(cvRound(holdtext.limcc),cvRound(holdtext.limcr)), cvRound(holdtext.limrad), CV_RGB(255,255,255),-1,8,0);
    cout << "ok here" << endl;
    cvCircle(mask,cvPoint(cvRound(holdtext.pupcc),cvRound(holdtext.pupcr)), cvRound(holdtext.puprad), CV_RGB(0,0,0),-1,8,0);
    cout << "ok here" << endl;
    //cvCircle(mask,cvPoint(cvRound(lfine[0]),cvRound(lfine[1])),cvRound(lfine[2]),CV_RGB(0,255,255),-1,8,0)
    //cvCircle(mask,cvPoint(cvRound(pupil[0]),cvRound(pupil[1])),cvRound(pupil[2]),CV_RGB(0,255,0),-1,8,0)
  }
  //cvNamedWindow("mask", CV_WINDOW_AUTOSIZE);
  //cvShowImage("mask", mask);
  cout << holdtext.pupcr << " " << holdtext.pupcc << " " << holdtext.puprad << " " << holdtext.limcr << " " << holdtext.limcc << " " << holdtext.limrad << endl;
  //point out where we think the center be.
  //cvCircle ( src,cvPoint(cvRound(holdtext.pupcr),cvRound(holdtext.pupcc)),2,CV_RGB(255,255,255),3,8,0);
  
  //now for the maths
  float pupcircum = 2 * holdtext.puprad * PI; //pi*r*2
  float step = 1.0/float(holdtext.puprad);  //=(2*PI)/pupcircum = 2pi/2pir = 1/r
  
  //build list of steps
  vector<float> theta_list; //[int(ceil(pupcircum))];
  vector<float>::iterator theta;
  
  k = 0;
  if (d)
  {
    cout << "ppc: " << pupcircum << endl;
    cout << "rad: " << holdtext.puprad << endl;
  }
  
  while (k <= pupcircum)
  {
    theta_list.push_back(k*step);
    //cout << theta_list[k] << endl;
    k++;
  }
  
  if (d) cout << theta_list.size() << endl;
  
  //make a temp image holder the with that many lines in it.
  IplImage* outtemp = cvCreateImage( cvSize (theta_list.size(),40), IPL_DEPTH_8U, 1);
  cvZero(outtemp);
  cout << outtemp->height << " " << outtemp->width << endl;
  theta = theta_list.begin();
  
  CvScalar p1, p2, p3, p4, test;
  vector<int> temp_results; //to store the unscaled line
  int x1temp, x2temp, y1temp, y2temp, result;
  
  //around circle
  //cvNamedWindow("outtemp", CV_WINDOW_AUTOSIZE);
  
  int around_count = 0; //measures where we are in theta ... Range: [0, theta_list.size()]
  
  for (theta = theta_list.begin() ; theta != theta_list.end() ;  theta++  )
  { 
    float r_expand = holdtext.puprad+1;
    //find the (x0,y0) of the first point
    //from pupil bound to limbic bound
    do // incr the radius until on the outside 
    {
      x1temp = int (holdtext.pupcc + floor(r_expand)   *cos(*theta) );
      y1temp = int (holdtext.pupcr + floor(r_expand)   *sin(*theta));
      x2temp = int (holdtext.pupcc + ceil(r_expand)*cos(*theta)+1);
      y2temp = int (holdtext.pupcr + ceil(r_expand)*sin(*theta)+1 );
      

      p1 = cvGet2D(src, y1temp, x1temp);
      p2 = cvGet2D(src, y1temp, x2temp);
      p3 = cvGet2D(src, y2temp, x1temp);
      p4 = cvGet2D(src, y2temp, x2temp);
      //average the 4 to get oversampling
      result = int(( double(p1.val[0])+double(p2.val[0])+double(p3.val[0])+double(p4.val[0]))/4.0f) ;

      temp_results.push_back(result);
      if (d) {}
      /*{
        cout << "vals: (" << p1.val[0] << " + "<< p2.val[0] << " + "<< p3.val[0] << " + "<< p4.val[0] << ")" << endl;
        cout << "x1 y1 x2 y2: (" << x1temp << ", " << y1temp << ") , (" << x2temp << ", "<< y2temp << ")" << endl;
        cout << "pixavg " << result << endl;
        cout << "r: " << r_expand << endl;        
        //cvShowImage("mask", mask);
        //cvShowImage("src", src);
        //cvWaitKey(0); 
      }*/
      r_expand++; //pop the radius bigger
      
      //map the pt for debug purposes:
      //cvCircle ( mask,cvPoint(cvRound(x2temp),cvRound(y2temp)),2,CV_RGB(255,255,255),3,8,0);
      //cvCircle ( src,cvPoint(cvRound(x2temp),cvRound(y2temp)),2,CV_RGB(255,255,255),3,8,0);
      
      
      test = cvGet2D(mask,y2temp,x2temp);  //test: are we still in iris area according to mask?
      
      if (d) {}
        //cout << test.val[0] << " ==? 255" << endl;
    } while(temp_results.size() == 1 || test.val[0] == 255    ); //while within the mask (other arg to make sure we get started inside the mask)
    
    if (d) cout << around_count <<  ": " << temp_results.size()  << "samples" << endl;
    
    //if (d) 
      //cvWaitKey(0);
    //now downsize the samples into a 40x1 line
    
    linetmpbig = cvCreateImage( cvSize (1,temp_results.size()), IPL_DEPTH_8U, 1);
    cvZero(linetmpbig);
    
    int tfer = 0;
    //put the line into an image
    //write from vector to unscaled image
    for (tfer=0 ; tfer <  temp_results.size() ; tfer++)
    {
      cvSet2D(linetmpbig, temp_results.size()-1-tfer, 0,cvScalar(temp_results[tfer],0,0,0) );
      p1 = cvGet2D(src, tfer, 0);
      
      //cout << "ltb: " << tfer<< "/" << temp_results.size() << ":  "<< p1.val[0] << endl;
      
    }
    cvResize(linetmpbig, linetmp);//scale the line to 40 tall
    
    if (d)
    {
      cvNamedWindow("linetmpbig", CV_WINDOW_AUTOSIZE);
      cvShowImage("linetmpbig", linetmpbig);
    
      cvNamedWindow("linetmp", CV_WINDOW_AUTOSIZE);
      cvShowImage("linetmp", linetmp);
    }
    
    //cout << linetmp->height << " " << linetmp->width << endl;
    
    //now we have a 1x40 line to stick into the array
    //set the whole line into the outtemp buffer, to be stretched
    tfer = 0;
    for (tfer=0 ; tfer <  40; tfer++)
    {
      p1 = cvGet2D(linetmp, tfer, 0);
      cvSet2D(outtemp, tfer, around_count ,cvScalar(p1.val[0],0,0,0) );
    }
    
    if (d)
    {
      cvShowImage("outtemp", outtemp);
      cvWaitKey(0);
      //int asdf;
      //for(asdf = 0 ; asdf < temp_results.size() ; asdf++)
        //cout << asdf << "/" << temp_results.size() << ":  "<< temp_results[asdf] << endl;
    }
    temp_results.clear();
    around_count ++;
  }
  //now, resize outtemp into out, display
  cvResize(outtemp, out);
/*
  cvShowImage("outtemp", outtemp);

  cvNamedWindow("out", CV_WINDOW_AUTOSIZE);
  cvShowImage("out", out);
  cvWaitKey(0);
  */
  cvSaveImage(outfilename,out);
  return 1;
}
